package taskmaster.auth;

import cgi.db.OracleConnect;
import taskmaster.util.SetEnvironment;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Hashtable;

/**
 * taskmaster.auth.Login
 * <BR>
 * Scope: application - so no data is stored except for name of db to connect to.
 * <BR><BR>
 * Used as the controller to log into the database.  Users are authenticated by their ability to connect to the specified oracle database with the passed username/password combo.
 */
public class Login extends taskmaster.util.RunStatement
{
    /**
     * No argument constructor for jsp page instantiation.
     */
    public Login()
    {
    }

    /**
     * Controller for logging in.
     * <BR>
     * This method calls the other 2 private methods in this class<BR><BR>
     * Otherwise returns true and modifies given user object to be logged in entirely
     *
     * @param user     The User object instantiated by the jsp pages for this session
     * @param userName try to log in using this userName
     * @param password try to log in using this password
     * @param host     connect to the db using this host (oci8)
     * @param database connect to this database (prod, dev, qa)
     * @return true if this userName/password combo can connect to this db on this host, without any exceptions, false otherwise.
     * @throws Exception known to be thrown if can't find db (tns error), can't log you in to that db (invalid userName/password), or can't find /omega/rc/tms.env file
     * @see validUserPass()
     * @see setRoles()
     */
    public boolean login(User user, String userName, String password, String host, String database) throws Exception
    {
        try
        {
            SetEnvironment.set();        // sets env vars for the whole system
            // this resets each time a user logs; effects the whole application
            if (user == null)
            {
                user = new User();
            }
            user.logout();
            if (!getConnection(user, userName, password, host, database))
            {
                return false;
            }
            if (!userName.equalsIgnoreCase("") && !password.equalsIgnoreCase(""))
            {
                user.setUsername(userName, password);
                setRoles(user);
            } else
            {
                user.setUsername(OracleConnect.getUserNameFromConnection(user.getConnection()), "");
                setRoles(user);
            }
            return true;
        }
        catch (Exception e)
        {
            String expl = "Exception taskmaster.auth.Login:login " + e;
            System.err.println(expl);
            throw new Exception(expl, e);
        }
    }

    /**
     * Attempts to connect to oracle with this user/pass combo
     */
    private boolean getConnection(User user, String userName, String password, String host, String database) throws Exception
    {
        if (userName == null || password == null || database == null)
        {
            return false;
        }

        Connection con = OracleConnect.getConnectionOps(userName, password, host, database);
        if (con == null)
        {
            return false;
        }
        if (con.isClosed())
        {
            System.err.println("Connection to db appears closed in getConnection");
        }
        user.setConnection(con, database);
        this.runStatement(user, "set role application");
        return true;
    }

    /**
     * Determines roles for this user based on his username.  Trusts that username in User user has been validated already.
     * A user is in a role if he's listed in tms.user_roles table as having:
     * <UL>
     * <LI>read only access (read)
     * <LI>write/read access (write)
     * <LI>admin/write/read access (admin)
     * </UL>
     *
     * @see taskmaster.auth.User.roles
     */
    private void setRoles(User user) throws SQLException
    {
        ResultSet rs = this.runStatement(user, "select ROLE_ID, ACCESS_LEVEL from tms.USER_ROLES where username='" + user.getUsername() + "'");
        rs.last();
        int rsSize = rs.getRow();
        rs.beforeFirst();
        Hashtable roles = new Hashtable(rsSize);

        while (rs.next())
        {
            roles.put(rs.getString("ROLE_ID"), rs.getString("ACCESS_LEVEL"));
        }

        user.setRoles(roles);
    }


    /**
     * provides a way to simply test whether a user/pass combo can connect to the dev database on oci8.
     * Will also give useful error messages if there's some problem problem with the databases this can check.
     * <UL>
     * <LI>arg0 - username
     * <LI>arg0 - password
     * <LI>arg0 - database (prod, qa, dev)
     * </UL>
     *
     * @param args command line arguments first arg (first is username, second is password, third is the database)
     */
    public static void main(String[] args)
    {
        try
        {
            Login l = new Login();
            User u = new User();
            boolean login = l.login(u, args[0], args[1], "oci8", args[2]);
            System.out.println("Connection to db closed?: " + u.getConnection().isClosed());
            System.out.println("Login worked?: " + login);
            System.out.print(u.getUsername());
        } catch (Exception e)
        {
            System.err.println("exception in main for login: " + e);
            e.printStackTrace();
        }
    }
}
